#ifndef __CONNECTION_HPP__
#define __CONNECTION_HPP__

#include "any.hpp"

/* 连接状态 */
typedef enum
{
    CONNECTING,    // 连接已建立，待处理状态或半连接状态。这是连接刚建立后，但各项准备工作如回调函数等都还未设置
    CONNECTED,     // 连接已建立，通讯状态。连接已建立且各项准备工作都已完成，可以通信
    DISCONNECTING, // 连接待关闭状态
    DISCONNECTED,  // 连接已关闭状态。
} ConnectionStatus;

class Connection;
using PtrConnection = std::shared_ptr<Connection>;

class Connection : public std::enable_shared_from_this<Connection>
{
    using ConnectedCallback = std::function<void(const PtrConnection &)>;
    using MessageCallback = std::function<void(const PtrConnection &, Buffer *)>;
    using CloseCallback = std::function<void(const PtrConnection &)>;
    using EventCallback = std::function<void(const PtrConnection &)>;

private:
    /* 真正的连接释放接口 */
    void ReleaseInLoop()
    {
        // 1. 修改连接状态
        _status = DISCONNECTED;
        // 2. 移除连接上的事件监控
        _channel.Remove();
        // 3. 关闭描述符
        _socket.Close();
        _sockfd = -1;
        // 4. 若当前定时器队列中还存在定时销毁任务，则取消这些任务
        if (_loop->HasTimer(_timer_id))
            DisableInactiveReleaseInLoop();
        // 5. 调用连接关闭的回调函数
         // 如果先调用服务器的连接关闭回调，Connection对象就会被释放，再调用Connection的连接关闭回调就是非法的行为，\
            所以需要先调用Connection的连接关闭回调
        if (_close_callback)
            _close_callback(shared_from_this());
        if (_server_close_callback)
            _server_close_callback(shared_from_this()); // 移除服务器管理的连接信息
    }
    /* 连接建立后，待处理状态阶段的处理行为 */
    void EstablishedInLoop()
    {
        // 1. 修改连接状态
        assert(_status == CONNECTING); // 当前状态必须是半连接状态
        _status = CONNECTED;
        // 2. 启动读事件监控
         // 一旦启动读事件监控，就可能立即触发读事件，若这时已启动非活跃连接销毁功能，就要刷新定时任务，\
            但如果将启动读事件监控放到构造函数中，就一定找不到定时任务，逻辑不合理
        _channel.EnableRead();
        // 3. 调用连接建立的回调函数
        if (_connected_callback)
            _connected_callback(shared_from_this());
    }
    /* 发送数据——将数据“发送”到发送缓冲区，启动写事件监控 */
    void SendInLoop(const Buffer buf)
    {
        if (_status == DISCONNECTED)
            return;

        // 1. 将数据放到发送缓冲区
        _out_buffer.WriteBuffer(buf);
        // 2. 启动写事件监控
        if (_channel.WriteAble() == false)
            _channel.EnableWrite();
    }
    /* 关闭连接——非实际关闭，要判断缓冲区中有没有数据待处理 */
    void ShutDownInLoop()
    {
        _status = DISCONNECTING;

        if (_in_buffer.ReadAbleSize() > 0)
        {
            if (_message_callback)
                _message_callback(shared_from_this(), &_in_buffer);
            // 可能存在情况：数据不完整，上层不处理，最终导致接收缓冲区中一直有数据，所以不管这个是否处理完，都关闭连接
        }
        if (_out_buffer.ReadAbleSize() > 0)
        {
            if (_channel.WriteAble() == false)
                _channel.EnableWrite();
        }
        if (_out_buffer.ReadAbleSize() == 0)
            // Release(); // 发送缓冲区没数据就直接关闭连接，不考虑接收缓冲区，因为可能接收缓冲区中有数据一直无法被处理
            ReleaseInLoop(); // 发送缓冲区没数据就直接关闭连接，不考虑接收缓冲区，因为可能接收缓冲区中有数据一直无法被处理
    }
    /* 启动非活跃链接销毁功能 */
    void EnableInactiveReleaseInLoop(int sec)
    {
        // 1. 设置非活跃链接销毁标志
        _enable_inactive_release = true;
        // 2. 添加定时销毁任务
        // 2.1 当前连接已存在定时销毁任务，刷新
        if (_loop->HasTimer(_timer_id))
            _loop->TimerRefresh(_timer_id);
        // 2.2 不存在，添加
        else
            // _loop->TimerAdd(_timer_id, sec, std::bind(&Connection::Release, this));
            _loop->TimerAdd(_timer_id, sec, std::bind(&Connection::ReleaseInLoop, this));
    }
    /* 关闭非活跃链接销毁功能 */
    void DisableInactiveReleaseInLoop()
    {
        // 1. 重置非活跃连接销毁标志
        _enable_inactive_release = false;
        // 2.
        _loop->TimerCancel(_timer_id);
    }
    /* 切换协议——重置上下文和阶段性处理函数 */
    void UpgradeInLoop(const Any &context, const ConnectedCallback &connected_callback, const MessageCallback &message_callback,
                       const CloseCallback &close_callback, const EventCallback &event_callback)
    {
        SetContext(context);
        SetConnectedCallback(connected_callback);
        SetMessageCallback(message_callback);
        SetCloseCallback(close_callback);
        SetEventCallback(event_callback);
    }

public:
    Connection(EventLoop *loop, uint64_t conn_id, int sockfd)
        : _loop(loop), _conn_id(conn_id), _timer_id(_conn_id), _sockfd(sockfd), _status(CONNECTING),
          _socket(_sockfd), _channel(_sockfd, _loop), _enable_inactive_release(false)
    {
        _channel.SetCloseCallback(std::bind(&Connection::HandleClose, this));
        _channel.SetErrorCallback(std::bind(&Connection::HandleError, this));
        _channel.SetEventCallback(std::bind(&Connection::HandleEvent, this));
        _channel.SetReadCallback(std::bind(&Connection::HandleRead, this));
        _channel.SetWriteCallback(std::bind(&Connection::HandleWrite, this));
    }
    ~Connection()
    {
        DBG_LOG("release conection: %p", this);
    }
    /* 获取连接关联的文件描述符 */
    int Fd() const
    {
        return _sockfd;
    }
    /* 获取连接id */
    int Id() const
    {
        return _conn_id;
    }
    /* 判断连接是否已建立（可通信状态） */
    bool Connected() const
    {
        return _status == CONNECTED;
    }
    /* 设置上下文 */
    void SetContext(const Any &context)
    {
        _context = context;
    }
    /* 获取上下文 */
    Any *GetContext()
    {
        return &_context;
    }
    /* 设置回调函数 */
    void SetConnectedCallback(const ConnectedCallback &cb)
    {
        _connected_callback = cb;
    }
    void SetMessageCallback(const MessageCallback &cb)
    {
        _message_callback = cb;
    }
    void SetCloseCallback(const CloseCallback &cb)
    {
        _close_callback = cb;
    }
    void SetEventCallback(const EventCallback &cb)
    {
        _event_callback = cb;
    }
    void SetSvrCloseCallback(const CloseCallback &cb)
    {
        _server_close_callback = cb;
    }
    /* 这五个是Channel相关的事件回调函数 */
    /* 描述符上可读事件被触发后调用的回调函数：接收socket中的数据到接收缓冲区，调用message_callback */
    void HandleRead()
    {
        // 1. 接收socket的数据放到接收缓冲区中
        char buffer[65536] = {0};
        ssize_t n = _socket.NonBlockRecv(buffer, 65535);
        if (n < 0) // 接收数据出错，关闭连接
        {
            // 不能直接关闭连接，因为缓冲区中可能还有数据需要处理
            return ShutDownInLoop();
        }
        // n为0表示 连接正常，但没接收到数据（socket接收缓冲区没数据）
        _in_buffer.Write(buffer, n); // 将数据放入到接收缓冲区
        // 2. 调用messgae_callback进行业务处理
        if (_in_buffer.ReadAbleSize() > 0) // 接收缓冲区中有数据待处理
        {
            return _message_callback(shared_from_this(), &_in_buffer);
            /* shared_from_this(): 获取当前对象自己的shared_ptr对象，且和之前存在的当前对象的shared_ptr对象共享所有权。
                要使用shared_from_this()需要当前类继承自std::enable_shared_from_this模板类。
                会生成一个当前类对象的weak_ptr对象，然后用这个weak_ptr对象获得shared_ptr对象 */
        }
    }
    /* 描述符上可写事件被触发后调用的回调函数：将发送缓冲区中的数据发送出去 */
    void HandleWrite()
    {
        ssize_t n = _socket.NonBlockSend(_out_buffer.ReadPosition(), _out_buffer.ReadAbleSize());
        if (n < 0) // 发送数据出错，关闭连接
        {
            if (_in_buffer.ReadAbleSize() > 0)
            {
                _message_callback(shared_from_this(), &_in_buffer);
            }
            // return Release(); // 真正释放连接
            return ReleaseInLoop(); // 真正释放连接
        }
        _out_buffer.MoveReadOffset(n);
        if (_out_buffer.ReadAbleSize() == 0)
        {
            _channel.DisableWrite(); // 发送缓冲区没数据，就不需要监视写事件了
            if (_status == DISCONNECTING)
                // return Release(); // 如果连接是待关闭状态，那么发送完数据后，若没数据，就释放连接
                return ReleaseInLoop(); // 如果连接是待关闭状态，那么发送完数据后，若没数据，就释放连接
        }
    }
    /* 描述符相应连接关闭事件被触发后调用的回调函数 */
    void HandleClose() // 关闭连接
    {
        if (_in_buffer.ReadAbleSize() > 0)
        {
            _message_callback(shared_from_this(), &_in_buffer);
        }
        // Release(); // 真正释放连接
        ReleaseInLoop(); // 真正释放连接
    }
    /* 描述符上错误事件被触发后调用的回调函数 */
    void HandleError()
    {
        return HandleClose();
    }
    /* 描述符上任意事件被触发后调用的回调函数 */
    void HandleEvent()
    {
        // 1. 刷新连接的活跃度
        if (_enable_inactive_release)
            _loop->TimerRefresh(_timer_id);
        // 2. 调用组件使用者的任意事件回调
        if (_event_callback)
            _event_callback(shared_from_this());
    }

    /* 连接建立后，待处理状态阶段的处理行为 */
    void Established()
    {
        _loop->RunInLoop(std::bind(&Connection::EstablishedInLoop, this));
    }
    /* 发送数据——将数据“发送”到发送缓冲区，启动写事件监控 */
    void Send(const char *data, size_t len)
    {
        /* 外界传入的data可能是临时数据，Send只是将发送操作压入了任务队列，而没有立即执行，在执行前，可能data指向的临时空间已经被销毁了 */
        Buffer buf;
        buf.Write(data, len);
        _loop->RunInLoop(std::bind(&Connection::SendInLoop, this, buf));
    }
    /* 关闭连接——非实际关闭，要判断缓冲区中有没有数据待处理 */
    void ShutDown()
    {
        _loop->RunInLoop(std::bind(&Connection::ShutDownInLoop, this));
    }
    /* 真正释放连接 */
    // void Release()
    // {
    //     _loop->QueueInLoop(std::bind(&Connection::ReleaseInLoop, this));
    // }
    /* 启动非活跃链接销毁功能 */
    void EnableInactiveRelease(int sec)
    {
        _loop->RunInLoop(std::bind(&Connection::EnableInactiveReleaseInLoop, this, sec));
    }
    /* 关闭非活跃连接销毁功能 */
    void DisableInactiveRelease()
    {
        _loop->RunInLoop(std::bind(&Connection::DisableInactiveReleaseInLoop, this));
    }
    /* 切换协议——重置上下文和阶段性回调处理函数 ——非线程安全，必须在EventLoop对应的线程中立即执行，
    防备新的事件触发后，处理新事件时，切换协议还没有被执行，就会导致数据仍然是使用原协议处理的 */
    void Upgrade(const Any &context, const ConnectedCallback &connected_callback, const MessageCallback &message_callback,
                 const CloseCallback &close_callback, const EventCallback &event_callback)
    {
        _loop->AssertInLoop();
        _loop->RunInLoop(std::bind(&Connection::UpgradeInLoop, this,
                                   context, connected_callback, message_callback, close_callback, event_callback));
    }

private:
    uint64_t _conn_id;             // 连接id
    uint64_t _timer_id;            // 定时器id，这里为了简化，使用_conn_id作为定时器id
    int _sockfd;                   // 连接关联的文件描述符
    ConnectionStatus _status;      // 连接的状态
    bool _enable_inactive_release; // 是否启动非活跃链接销毁功能
    EventLoop *_loop;              // 连接所关联的EventLoop
    Socket _socket;                // 套接字的操作管理
    Channel _channel;              // 连接事件监控的管理
    Buffer _in_buffer;             // 输入缓冲区——存放从socket中读取到的数据
    Buffer _out_buffer;            // 输出缓冲区——存放要发送给对端的数据
    Any _context;                  // 上下文
    /* 这四个回调函数由服务器模块设置（服务器模块的回调又是由使用者设置） */
    ConnectedCallback _connected_callback; // 连接建立的回调函数
    MessageCallback _message_callback;     // 数据到来的回调函数
    CloseCallback _close_callback;         // 连接关闭的回调函数
    EventCallback _event_callback;         // 任意事件产生的回调函数
    /* 这个回调函数是在组件内使用，因为服务器组件内会把所有连接管理起来，一旦某个连接要关闭，就应该从管理连接的地方移除掉这个连接的信息 */
    CloseCallback _server_close_callback;
};

#endif